# -*- coding: utf-8 -*-
# @Author: QX
# @File: keyworks.py
import base64
import logging

import allure
import jsonpath
import requests
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from deepdiff import DeepDiff
from pymysql import cursors
import pymysql

from core.globalContext import g_context
gc = g_context()

class Keywords:

    @allure.step('发送POST请求')
    def request_post(self, **kwargs):
        response=requests.post(**kwargs)
        gc.set_dict('current_response',response)
        #print('request_post%%%%%%%%%%%%', response.json())
        return response

    # @allure.step('发送GET请求')
    # def request_get(self, **kwargs):
    #     response = requests.get(**kwargs)
    #     gc.set_dict('current_response', response)
    #     return response

    @allure.step("参数数据：发送Get请求")
    def request_get(self, **kwargs):
        url = kwargs.get("URL", None)
        params = kwargs.get("PARAMS", None)
        headers = kwargs.get("HEADERS", None)

        request_data = {
            "url": url,
            "params": params,
            "headers": headers,
        }

        response = requests.get(**request_data)
        g_context().set_dict("current_response", response)  # 默认设置成全局变量
        return response
        print("-----------------------")
        print(response.json())
        print("-----------------------")

    @allure.step('发送PUT请求')
    def request_put(self, **kwargs):
        response = requests.put(**kwargs)
        gc.set_dict('current_response', response)
        return response

    @allure.step('发送post请求--form_data表单的请求')
    def request_post_form_data(self, **kwargs):
        url = kwargs.get('URL',None)

        params = kwargs.get('PARAMS', None)
        data = kwargs.get('DATA', None)
        headers = kwargs.get('HEADERS', None)
        request_data={
            'url': url,
            'params': params,
            'data': data,
            'headers': headers
        }

        response = self.request_post(**request_data)
        return response

    @allure.step('发送post请求--form_json表单的请求')
    def request_post_form_json(self, **kwargs):
        url = kwargs.get('URL', None)

        params = kwargs.get('PARAMS', None)
        data = kwargs.get('DATA', None)
        headers = kwargs.get('HEADERS', None)
        request_data = {
            'url': url,
            'params': params,
            'json': data,
            'headers': headers
        }

        response = self.request_post(**request_data)
        return response

    @allure.step("参数数据：发送Post请求-form_data--文件上传的请求")
    def request_post_form_urlencoded(self, **kwargs):
        """
        发送Post请求
        """
        url = kwargs.get("URL", None)
        files = kwargs.get("FILES", None)
        params = kwargs.get("PARAMS", None)
        headers = kwargs.get("HEADERS", None)
        data = kwargs.get("DATA", None)

        request_data = {
            "url": url,
            "params": params,
            "headers": headers,
            "data": data,
            "files": eval(files)  # 变成字典格式
        }

        response = requests.post(**request_data)
        print("response", response.json())
        g_context().set_dict("current_response", response)  # 默认设置成全局变量
        return response
        print("-----------------------")
        print(response.json())
        print("-----------------------")

    @allure.step("参数数据：发送Delete请求")
    def request_delete(self, **kwargs):
        """
        发送Post请求
        """
        url = kwargs.get("URL", None)
        params = kwargs.get("PARAMS", None)
        headers = kwargs.get("HEADERS", None)
        data = kwargs.get("DATA", None)

        request_data = {
            "url": url,
            "params": params,
            "headers": headers,
            "json": data,
        }

        response = requests.delete(**request_data)
        g_context().set_dict("current_response", response)  # 默认设置成全局变量
        return response
        print("-----------------------")
        print(response.json())
        print("-----------------------")


    @allure.step('提取响应数据并存储到全局变量')
    def ex_jsonData(self, **kwargs):
        '''
        提取json数据
        EXVALUE：提取json的表达式
        INDEX: 非必填，默认为0
        VARNAME：存储的变量名，方便后面使用
        从响应结果中提取值，并放到全局变量中
        :return:
        '''
        #1.获得jsonpath的表达式
        EXPRESSION=kwargs.get("EXVALUE",None)
        #2.获得下标
        INDEX = kwargs.get("INDEX", None)
        if INDEX  is None:
            INDEX = 0

        #dict的全局变量中获得响应结果
        try:

            response = gc.get_dict('current_response').json()
            print('response$$$$$$$$$$$$', response)
        except  Exception  as e:
            logging.basicConfig(level=logging.ERROR)
            logging.error(f"=====获得想要的结果失败--失败的原因{e}=====")

        else:
            logging.basicConfig(level=logging.INFO)
            logging.info(f"=====获得想要的结果成功=====")

        ex_data=jsonpath.jsonpath(response, EXPRESSION)[INDEX]
        print('ex_data!!!!!!!!!!!!', ex_data)
        gc.set_dict(kwargs['VARNAME'],ex_data)
        print('token&&&&&&&&&&=',gc.show_dic())
        return ex_data

    @allure.step('响应结果中部分内容的断言')
    def assert_text_comparators(self, **kwargs):
        '''
        封装断言以进行不同的比较操作。

        参数:
        value (Any): 要比较的值。
        expected (Any): 预期的值。
        op_str (str): 操作符的字符串表示（如 '>', '<', '==' 等）。
        message (str, optional): 自定义的错误消息。

        返回:
        None: 如果断言成功，则不返回任何内容。

        引发:
        AssertionError: 如果断言失败
        '''
        comparators = {
            '>': lambda a, b: a > b,
            '<': lambda a, b: a < b,
            '==': lambda a, b: a == b,
            '>=': lambda a, b: a >= b,
            '<=': lambda a, b: a <= b,
            '!=': lambda a, b: a != b,
        }

        message = kwargs.get('MESSAGE', None)
        if kwargs['OP_STR'] not in comparators:
            raise ValueError(f"没有该操作方式: {kwargs['OP_STR']}")

        if not comparators[kwargs['OP_STR']](kwargs['VALUE'], kwargs["EXPECTED"]):

            if message:
                raise AssertionError(message)
            else:
                raise AssertionError(f"{kwargs['VALUE']} {kwargs['OP_STR']} {kwargs['EXPECTED']} 失败")

    # TODO: 扩展 - JSOND断言方法
    @allure.step("参数数据：JSOND断言文本内容")
    def assert_json_comparators(self, **kwargs):
        """
        封装断言以进行不同的比较操作。

        参数:
        value (Any): 要比较的jsonPath值。
        expected (Any): 预期的值。
        op_str (str): 操作符的字符串表示（如 '>', '<', '==' 等）。
        message (str, optional): 自定义的错误消息。

        返回:
        None: 如果断言成功，则不返回任何内容。

        引发:
        AssertionError: 如果断言失败。
        """
        comparators = {
            '>': lambda a, b: a > b,
            '<': lambda a, b: a < b,
            '==': lambda a, b: a == b,
            '>=': lambda a, b: a >= b,
            '<=': lambda a, b: a <= b,
            '!=': lambda a, b: a != b,
        }

        message = kwargs.get("MESSAGE", None)

        if kwargs["OP_STR"] not in comparators:
            raise ValueError(f"没有该操作方式: {kwargs['OP_STR']}")

        # 通过jsonpath获取对应的数据
        # 获取响应数据
        response = g_context().get_dict("current_response").json()
        ex_data = jsonpath.jsonpath(response, kwargs['VALUE'])[0] # 默认就取第一个

        if not comparators[kwargs['OP_STR']](ex_data, kwargs["EXPECTED"]):
            if message:
                raise AssertionError(message)
            else:
                raise AssertionError(f"{ex_data} {kwargs['OP_STR']} {kwargs['EXPECTED']} 失败")


    @allure.step("全量断言-对比两个Json的差异")
    def assert_json_DeepDiff(self, **kwargs):
        """
        对比两个json的差异
        :param json1: 期望结果
        :param json2: 实际结果
        :param exclude_paths:需要排除的字段，集合的类型，比如{“id”,...}
        :param ignore_order: 忽略顺序，一般用户有序数据类型，比如列表
        :param ignore_string_case:忽略值的大小写，False
        :return: 当数据没有差异则返回空集合

        """
        try:
            json1 = kwargs["json1"]
            json2 = kwargs["json2"]

            exclude_paths = kwargs.get("过滤字段", False)
            ignore_order = kwargs.get("忽略顺序", False)
            ignore_string_case = kwargs.get("忽略大小写", False)

            screen_data = {"exclude_paths": exclude_paths, "ignore_order": ignore_order,
                       "ignore_string_case": ignore_string_case}

            diff = DeepDiff(json1, json2, **screen_data)
        except Exception  as e:
            assert  False ,  f"全量断言失败，代码报错{e}"

        assert not diff, f"全量断言失败:{diff}"

        # TODO: 扩展 - 数据库提取的方法

    @allure.step("参数数据：提取数据库数据并存储")
    def ex_mysqlData(self, **kwargs):
        """
        数据库 : 数据库的名称
        SQL:
        引用变量：数据库要存储的变量名，列表格式
        存储到全局变量：{“变量名_下标”:数据}
        """
        config = {"cursorclass": cursors.DictCursor}
        db_config = g_context().get_dict("_database")['mysql001']
        config.update(db_config)
        con = pymysql.connect(**config)
        cur = con.cursor()
        cur.execute(kwargs['SQL'])
        rs = cur.fetchall()
        cur.close()
        con.close()
        #存储到全局变量：{“变量名_下标”:数据}
        # 在Python中，enumerate是一个内置函数，
        # 它可以同时返回一个可迭代对象的元素和它们的索引
        var_names = kwargs['引用变量'].split(",")

        result = {}
        for i , data in enumerate(rs  , start=1):
            for j , value  in  enumerate(var_names):
                result[f'{var_names[j]}_{i}']  = data.get(var_names[j])
                print('$$$$====result===', result)
                print('####data#####' , data)
                print('####value#####', value)
        g_context().set_by_dict(result)

    def encrypt_aes(self,**kwargs):
        '''

        :param data: 需要加密的数据
        :param VARNAME: 存储加密之后的内容，可以放入全局变量中
        :return:
        '''
        key = b"1234567812345678"
        # 1.将输入的数据转换为 UTF-8 编码的字节形式。
        #data = data.encode('utf-8')
        data = kwargs['data'].encode('utf-8')
        # 2.使用指定的密钥和 ECB 模式创建一个 AES 加密器对象。
        cipher = AES.new(key, AES.MODE_ECB)
        # 3.使用 PKCS7 填充算法对数据进行填充，然后使用加密器对象对填充后的数据进行加密，
        ct_bytes = cipher.encrypt(pad(data, AES.block_size))
        # 得到密文字节数组。
        # 4.将密文字节数组进行 Base64 编码，并将结果从字节形式还原为 UTF-8 编码的字符串形式返回。
        encrypt_data = base64.b64encode(ct_bytes).decode('utf-8')
        g_context().set_dict(kwargs["VARNAME"], encrypt_data)







